#ifndef _Reorder_Buffer
#define _Reorder_Buffer
#include <iostream>
#include <cstring>
#include <cstdio>
#include "store.hpp"
#include "arithmetic.hpp"
#include "tomasulo_cpu.hpp"

void CPU::ROB_run()
{
    #ifdef func
        puts("");
        puts("---ROB_run---");
    #endif 


    CPU::ROB_new = CPU::ROB_prev;

    if (CPU::commit_signal < 0)
    {
        CPU::ROB_new.h.clear();
        return;
    }

    if (CPU::commit_signal > 0)
        CPU::ROB_new.h.pop();

    if (!ROB_new.h.empty())
    {
        CPU::Reorder_Buffer::node &tmp = CPU::ROB_prev.h.get_front();
        CPU::Reorder_Buffer::node &p_prev = 
        (tmp.iscommit) ? CPU::ROB_prev.h.get_sec() : CPU::ROB_prev.h.get_front();
        CPU::Reorder_Buffer::node &p_new = CPU::ROB_new.h.get_front();

        if (p_prev.ready)
        {
            p_new.iscommit = 1;
            CPU::ROB_to_commit = p_new;
        }
    }

    const CPU::Reorder_Buffer::node &t = CPU::issue_to_ROB;
    if (~t.opcode)
        CPU::ROB_new.h.push(t);

    CPU::CDB c = CPU::ex_to_CDB;
    #ifdef func
        puts("ex_to_CDB");
        c.print();
    #endif 
    if (~c.dest)
    {
        CPU::ROB_new.h.node[c.dest].val = c.val;
        CPU::ROB_new.h.node[c.dest].pc = c.pos;
        CPU::ROB_new.h.node[c.dest].isjump = c.isjump;
        CPU::ROB_new.h.node[c.dest].ready = 1;
    }

    c = CPU::LSB_to_CDB_prev;
    #ifdef func
        puts("LSB_to_CDB");
        c.print();
    #endif 
    if (~c.dest)
    {
        CPU::ROB_new.h.node[c.dest].val = c.val;
        CPU::ROB_new.h.node[c.dest].ready = 1;
    }
        
    #ifdef func
        puts("issue_to_ROB");
        t.print();
    #endif 

    #ifdef func
        puts("-------------");
        puts("");
    #endif 
}

void CPU::ROB_commit()
{
    #ifdef func
        puts("");
        puts("---ROB_commit---");
    #endif 
    CPU::Reorder_Buffer::node &t = CPU::ROB_to_commit;

    #ifdef func
        puts("ROB_to_commit");
        t.print();
    #endif 

    if (!t.iscommit)
        return;

    #ifdef debug2
        printf("opcode:%d func3:%d func7:%d\n", t.opcode, t.func3, t.func7);
    #endif

    if (t.opcode == 55) // LUI
    {
        #ifdef debug2
            printf("lui %d %d\n", t.dest, t.val);
        #endif
        
    }
    else if (t.opcode == 23) // AUIPC
    {
        #ifdef debug2
            printf("auipc %d %d\n", t.dest, t.val);
        #endif
    }
    else if (t.opcode == 111) // JAL
    {
        #ifdef debug2
            printf("jal %d %d\n", t.dest, t.val);
        #endif
    }
    else if (t.opcode == 103) // JALR
    {
        #ifdef debug2
            printf("jalr %d %d\n", t.dest, t.val);
        #endif
    }
    else if (t.opcode == 99) // BEQ ~ BGEU
    {
        if (t.func3 == 0) // BEQ
        {
            #ifdef debug2
                printf("beq %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 1) // BNE
        {
            #ifdef debug2
                printf("bne %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 4) // BLT
        {
            #ifdef debug2
                printf("blt %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 5) // BEG
        {
            #ifdef debug2
                printf("beg %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 6) //BLTU
        {
            #ifdef debug2
                printf("bltu %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 7) // BGEU
        {
            #ifdef debug2
                printf("bgeu %d %d\n", t.dest, t.val);
            #endif
        }
    }
    else if (t.opcode == 3) // LB ~ LHU
    {
        #ifdef debug2
            printf("lb ~ lhu %d %d\n", t.dest, t.val);
        #endif
    }
    else if (t.opcode == 35) // SB ~ SW
    {
        #ifdef debug2
            printf("sb ~ sw %d %d\n", t.dest, t.val);
        #endif
    }
    else if (t.opcode == 19) // ADDI ~ SRAI
    {
        if (t.func3 == 0) // ADDI
        {
            #ifdef debug2
                printf("addi %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 2) // SLTI
        {
            #ifdef debug2
                printf("slti %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 3) // STLIU
        {
            #ifdef debug2
                printf("stliu %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 4) // XORI
        {
            #ifdef debug2
                printf("xori %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 6) // ORI
        {
            #ifdef debug2
                printf("ori %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 7) // ANDI
        {
            #ifdef debug2
                printf("andi %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 1) // SLLI
        {
            #ifdef debug2
                printf("slli %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 5) // SRLI ~ SRAI
        {
            if (t.func7 == 0) // SRLI
            {
                #ifdef debug2
                    printf("srli %d %d\n", t.dest, t.val);
                #endif
            }
            else if (t.func7 == 32) // SRAI
            {
                #ifdef debug2
                    printf("srai %d %d\n", t.dest, t.val);
                #endif
            }
        }
    }
    else if (t.opcode == 51) // ADD ~ AND
    {
        if (t.func3 == 0) // ADD ~ SUB
        {
            if (t.func7 == 0) // ADD
            {
                #ifdef debug2
                    printf("add %d %d\n", t.dest, t.val);
                #endif
            }
            else if (t.func7 == 32) // SUB
            {
                #ifdef debug2
                    printf("sub %d %d\n", t.dest, t.val);
                #endif
            }
        }
        else if (t.func3 == 1) // SLL
        {
            #ifdef debug2
                printf("sll %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 2) // SLT
        {
            #ifdef debug2
                printf("slt %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 3) // SLTU
        {
            #ifdef debug2
                printf("sltu %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 4) // XOR
        {
            #ifdef debug2
                printf("xor %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 5) // SRL ~ SRA
        {
            if (t.func7 == 0) // SRL
            {
                #ifdef debug2
                    printf("srl %d %d\n", t.dest, t.val);
                #endif
            }
            else if (t.func7 == 32) // SRA
            {
                #ifdef debug2
                    printf("sra %d %d\n", t.dest, t.val);
                #endif
            }
        }
        else if (t.func3 == 6) // OR
        {
            #ifdef debug2
                printf("or %d %d\n", t.dest, t.val);
            #endif
        }
        else if (t.func3 == 7) // AND
        {
            #ifdef debug2
                printf("and %d %d\n", t.dest, t.val);
            #endif
        }
    }

    CPU::commit_signal = 1;
    if (~t.dest)
        CPU::commit_to_reg = CPU::CDB(t.dest, t.val, t.pos);
    else if (t.val)
        CPU::commit_to_LSB = 1;

    if (t.isjump)
        CPU::commit_signal = -t.pc;

    t.iscommit = 0;
    #ifdef func
        puts("----------------");
        puts("");
    #endif 
}

#endif